﻿
'not maintain anymore, the newer version is in urlshorten project
Imports System.IO
Imports std
Imports storoomc
Imports xmlctrl
Imports xhtmlctrl
Imports webctrl

Public Class urlshorten
    Inherits controlProcessor

    Public Const _pathidentity As String = "/us/"
    Public Const maxId As Byte = 62
    Private randomkeylen As Int32

    Private Class storoomserver
        Public Shared host As String
        Public Shared port As Int32
        Public Shared retrytimes As Int64
    End Class

    Public Class query
        Public Const url As String = "u"
        Public Const key As String = "k"
        Public Const action As String = "a"
    End Class

    Public Overrides Function pathidentity() As String
        Return _pathidentity
    End Function

    Private Function getstoroom(ByRef host As String, ByRef port As Int32, ByRef retrytimes As Int64) As Boolean
        copy(host, storoomserver.host)
        copy(port, storoomserver.port)
        copy(retrytimes, storoomserver.retrytimes)

        Return True
    End Function

    Public Overrides Sub initial(Optional ByVal config As configParser = Nothing)
        MyBase.initial(config)
        storoomserver.host = config.parameter(configNaming.urlshorten.name, configNaming.urlshorten.storoomhost _
                                              , configNaming.defaultValue.urlshorten.storoomhost)
        storoomserver.port = config.parameter(configNaming.urlshorten.name, configNaming.urlshorten.storoomport _
                                                , configNaming.defaultValue.urlshorten.storoomport)
        storoomserver.retrytimes = config.parameter(configNaming.urlshorten.name _
                                                    , configNaming.urlshorten.storoomretrytimes _
                                                    , configNaming.defaultValue.urlshorten.storoomretrytimes)
        randomkeylen = config.parameter(configNaming.urlshorten.name _
                                         , configNaming.urlshorten.randomKeyLength _
                                         , configNaming.defaultValue.urlshorten.randomKeyLength)

        storoom.initial(AddressOf getstoroom)
    End Sub

    Public Enum action
        submit = 0
        preview = 1
        delayJump = 2
        jump = 3
        urlKeyMapping = 4

        unknown = 65535
    End Enum

    Public ReadOnly actionVerbs() As String = {"sbt", "prv", "djp", "jmp", "ukm"}

    '/su/{key}  jump
    '/su/?u={url}&k={key}&a={action}

    Private Function isInvalidKey(ByVal key As String)
        Return isemptystring(key)
    End Function

    Private Function id2char(ByVal id As Byte) As Char
        assert(id >= 0 AndAlso id < maxId, "should not have an id which is not in range [0,62).")

        If id < 10 Then
            Return Convert.ToChar(id + Convert.ToInt32(character._0))
        ElseIf id < 36 Then
            Return Convert.ToChar(id + Convert.ToInt32(character.a) - 10)
        ElseIf id < 62 Then
            Return Convert.ToChar(id + Convert.ToInt32(Char.ToUpper(character.a)) - 36)
        Else
            assert(False, "the maxId has not been included in id2char.")
        End If
    End Function

    Private Function generateRandomKey() As String
        Dim i As Int32
        Dim rtn As String = Nothing
        For i = 0 To randomkeylen - 1
            rtn += id2char(rnd(0, maxId, True))
        Next

        Return rtn
    End Function

    Private Function fallback(ByVal context As httpContext, ByVal page As xhtmlpage, ByVal key As String) As Boolean
        page.body().text = "cannot find records about key " + key _
                            + ", this page will automatically jump to homepage of urlshorten service in 10 seconds."
        delayJump_homepage(context, page)

        Return True
    End Function

    Private Function jump(ByVal context As httpContext, ByVal page As xhtmlpage _
                          , ByVal key As String, ByVal delay As Int64) As Boolean
        assert(Not page Is Nothing, "page is a nothing xhtmlpage instance.")

        Dim url As String = Nothing
        If previewImpl(context, page, key, url) Then
            page.title().text = "jump & delayjump - " + applicationSign()

            page.body().addchild(context.createControl(New br(Nothing)))

            Dim xmltext As xmltext = Nothing
            xmltext = context.createControl(New xmltext())
            xmltext.text = "now automatically jump will happen in " + Convert.ToString(delay) + " seconds"

            Dim delayjump As delayJump = Nothing
            delayjump = context.createControl(New delayJump(Nothing))
            delayjump.delay = delay
            delayjump.destination = url
            page.head().addchild(delayjump)
            Return True
        Else
            Return fallback(context, page, key)
        End If
    End Function

    Private Function previewImpl(ByVal context As httpContext, ByVal page As xhtmlpage _
                                 , ByVal key As String, ByRef url As String) As Boolean
        assert(Not page Is Nothing, "page is a nothing xhtmlpage instance.")

        Dim bt As bytes = Nothing
        page.title().text = "preview - " + applicationSign()
        If storoom.read(key, bt) Then
            url = encoding.utf8.GetString(+bt)

            Dim xmltext As xmltext = Nothing
            xmltext = context.createControl(New xmltext())
            xmltext.text = "the key you have provided is " + key
            page.body().addchild(xmltext)
            page.body().addchild(context.createControl(New br(Nothing)))

            xmltext = context.createControl(New xmltext())
            xmltext.text = "urlshorten service can accept following forms to this key for jump mode."
            page.body().addchild(xmltext)
            page.body().addchild(context.createControl(New br(Nothing)))

            Dim a As a = Nothing
            a = context.createControl(New a(Nothing))
            a.text = key
            a.href = a.text
            page.body().addchild(a)
            page.body().addchild(context.createControl(New br(Nothing)))

            a = context.createControl(New a(Nothing))
            a.text = network.thislevelPathMark _
                    + urlUtils.combineQuery(query.key, key, query.action, actionVerbs(action.jump))
            a.href = a.text
            page.body().addchild(a)
            page.body().addchild(context.createControl(New br(Nothing)))

            xmltext = context.createControl(New xmltext())
            xmltext.text = "and can accept following form to this key for delay jump mode."
            page.body().addchild(xmltext)
            page.body().addchild(context.createControl(New br(Nothing)))

            a = context.createControl(New a(Nothing))
            a.text = network.thislevelPathMark _
                    + urlUtils.combineQuery(query.key, key, query.action, actionVerbs(action.delayJump))
            a.href = a.text
            page.body().addchild(a)
            page.body().addchild(context.createControl(New br(Nothing)))

            xmltext = context.createControl(New xmltext())
            xmltext.text = "this page will automatically jump to "
            page.body().addchild(xmltext)

            a = context.createControl(New a(Nothing))
            a.text = url
            a.href = url
            page.body().addchild(a)

            xmltext = context.createControl(New xmltext())
            xmltext.text = " in jump and delayjump mode."
            page.body().addchild(xmltext)
            Return True
        Else
            Return False
        End If
    End Function

    Private Function preview(ByVal context As httpContext, ByVal page As xhtmlpage, ByVal key As String) As Boolean
        If Not previewImpl(context, page, key, Nothing) Then
            Return fallback(context, page, key)
        Else
            Return True
        End If
    End Function

    Private Sub delayJump_homepage(ByVal context As httpContext, ByVal page As xhtmlpage)
        assert(Not context Is Nothing, "context is a nothing httpContext instance.")
        assert(Not page Is Nothing, "page is a nothing xhtmlpage instance.")

        Dim delayjump As delayJump = Nothing
        delayjump = context.createControl(New delayJump(Nothing))
        delayjump.delay = 10
        delayjump.destination = network.thislevelPathMark
        page.head().addchild(delayjump)
    End Sub

    Private Sub delayJump_preview(ByVal context As httpContext, ByVal page As xhtmlpage, ByVal key As String)
        assert(Not context Is Nothing, "context is a nothing httpContext instance.")
        assert(Not page Is Nothing, "page is a nothing xhtmlpage instance.")

        Dim delayjump As delayJump = Nothing
        delayjump = context.createControl(New delayJump(Nothing))
        delayjump.delay = 10
        delayjump.destination = network.thislevelPathMark _
                                + urlUtils.combineQuery(query.key, key, query.action, actionVerbs(action.preview))
        page.head().addchild(delayjump)
    End Sub

    Private Function submit(ByVal context As httpContext, ByVal page As xhtmlpage _
                            , ByVal key As String, ByVal url As String) As Boolean
        assert(Not context Is Nothing, "context is a nothing httpContext instance.")
        assert(Not page Is Nothing, "page is a nothing xhtmlpage instance.")

        page.title().text = "submit result - " + applicationSign()

        If isInvalidKey(key) Then
            Dim xmltext As xmltext = Nothing
            xmltext = context.createControl(New xmltext())
            xmltext.text = "the key you have submited " + key + " is invalid."
            page.body().addchild(xmltext)
            page.body().addchild(context.createControl(New br(Nothing)))

            xmltext = context.createControl(New xmltext())
            xmltext.text = "please try another one."
            page.body().addchild(xmltext)
            page.body().addchild(context.createControl(New br(Nothing)))

            xmltext = context.createControl(New xmltext())
            xmltext.text = "this page will automatically jump to homepage in 10 seconds."
            page.body().addchild(xmltext)

            delayJump_homepage(context, page)
            Return True
        End If

        If storoom.seek(key) Then
            Dim xmltext As xmltext = Nothing
            xmltext = context.createControl(New xmltext())
            xmltext.text = "the key you have submited " + key + " has been registered."
            page.body().addchild(xmltext)
            page.body().addchild(context.createControl(New br(Nothing)))

            xmltext = context.createControl(New xmltext())
            xmltext.text = "this page will automatically jump to preview page of this key in 10 seconds."
            page.body().addchild(xmltext)

            delayJump_preview(context, page, key)
            Return True
        End If

        If storoom.modify(key, New bytes(encoding.utf8.GetBytes(url))) _
                 AndAlso storoom.write(url, New bytes(encoding.utf8.GetBytes(key + character.newline))) Then
            Dim xmltext As xmltext = Nothing
            xmltext = context.createControl(New xmltext())
            xmltext.text = "success to store key-url pair, " + key + " - " + url
            page.body().addchild(xmltext)
            page.body().addchild(context.createControl(New br(Nothing)))

            xmltext = context.createControl(New xmltext())
            xmltext.text = "this page will automatically jump to preview page of this key in 10 seconds."
            page.body().addchild(xmltext)

            delayJump_preview(context, page, key)
        Else
            Dim xmltext As xmltext = Nothing
            xmltext = context.createControl(New xmltext())
            xmltext.text = "internal error to register key " + key + " & url " + url
            page.body().addchild(xmltext)
            page.body().addchild(context.createControl(New br(Nothing)))

            xmltext = context.createControl(New xmltext())
            xmltext.text = "please try again later."
            page.body().addchild(xmltext)
            page.body().addchild(context.createControl(New br(Nothing)))

            xmltext = context.createControl(New xmltext())
            xmltext.text = "this page will automatically jump to homepage in 10 seconds."
            page.body().addchild(xmltext)

            delayJump_homepage(context, page)
        End If

        Return True
    End Function

    Private Sub homepage_submit(ByVal context As httpContext, ByVal root As xmlcontrol)
        assert(Not context Is Nothing, "context is a nothing httpContext instance.")
        assert(Not root Is Nothing, "root is a nothing xmlcontrol instance.")

        Dim form As form = Nothing
        form = context.createControl(New form(Nothing))
        form.id = "id_sbt_frm"
        form.action = network.thislevelPathMark
        form.method = xhtmlControl_1_1.method_enum.get
        root.addchild(form)

        Dim label As label = Nothing
        label = context.createControl(New label(Nothing))
        label.id = "id_sbt_ul"
        label.text = "input a url first."
        form.addchild(label)

        Dim input As input = Nothing
        input = context.createControl(New input(Nothing))
        input.type = xhtmlctrl.input.type_enum.text
        input.name = query.url
        input.value = network.httpAddressHeader
        input.class = "c_ui"
        form.addchild(input)

        label = context.createControl(New label(Nothing))
        label.id = "id_sbt_kl"
        label.text = "then input a shorten form [key] you prefered, " _
                    + "urlshorten service has already generated one for you."
        form.addchild(label)

        input = context.createControl(New input(Nothing))
        input.type = xhtmlctrl.input.type_enum.text
        input.name = query.key
        input.value = generateRandomKey()
        input.class = "c_ki"
        form.addchild(input)

        input = context.createControl(New input(Nothing))
        input.type = xhtmlctrl.input.type_enum.submit
        input.class = "c_s"
        input.value = "submit"
        form.addchild(input)

        input = context.createControl(New input(Nothing))
        input.type = xhtmlctrl.input.type_enum.hidden
        input.name = query.action
        input.value = actionVerbs(action.submit)
        form.addchild(input)
    End Sub

    Private Sub homepage_preview(ByVal context As httpContext, ByVal root As xmlcontrol)
        assert(Not context Is Nothing, "context is a nothing httpContext instance.")
        assert(Not root Is Nothing, "root is a nothing xmlcontrol instance.")

        Dim form As form = Nothing
        form = context.createControl(New form(Nothing))
        form.id = "id_prv_frm"
        form.action = network.thislevelPathMark
        form.method = xhtmlControl_1_1.method_enum.get
        root.addchild(form)

        Dim input As input = Nothing
        input = context.createControl(New input(Nothing))
        input.type = xhtmlctrl.input.type_enum.text
        input.name = query.key
        input.class = "c_ki"
        form.addchild(input)

        input = context.createControl(New input(Nothing))
        input.type = xhtmlctrl.input.type_enum.submit
        input.class = "c_s"
        input.value = "preview"
        form.addchild(input)

        input = context.createControl(New input(Nothing))
        input.type = xhtmlctrl.input.type_enum.hidden
        input.name = query.action
        input.value = actionVerbs(action.preview)
        form.addchild(input)
    End Sub

    Private Sub homepage_jump(ByVal context As httpContext, ByVal root As xmlcontrol, ByVal delayjump As Boolean)
        assert(Not context Is Nothing, "context is a nothing httpContext instance.")
        assert(Not root Is Nothing, "root is a nothing xmlcontrol instance.")

        Dim cssname As String = Nothing
        If delayjump Then
            cssname = "djp"
        Else
            cssname = "jmp"
        End If

        Dim form As form = Nothing
        form = context.createControl(New form(Nothing))
        form.id = "id_" + cssname + "_frm"
        form.action = network.thislevelPathMark
        form.method = xhtmlControl_1_1.method_enum.get
        root.addchild(form)

        Dim input As input = Nothing
        input = context.createControl(New input(Nothing))
        input.type = xhtmlctrl.input.type_enum.text
        input.name = query.key
        input.class = "c_ki"
        form.addchild(input)

        input = context.createControl(New input(Nothing))
        input.type = xhtmlctrl.input.type_enum.submit
        input.class = "c_s"
        If delayjump Then
            input.value = "delay jump"
        Else
            input.value = "jump"
        End If
        form.addchild(input)

        input = context.createControl(New input(Nothing))
        input.type = xhtmlctrl.input.type_enum.hidden
        input.name = query.action
        If delayjump Then
            input.value = actionVerbs(action.delayJump)
        Else
            input.value = actionVerbs(action.jump)
        End If
        form.addchild(input)
    End Sub

    Private Sub homepage_urlkey(ByVal context As httpContext, ByVal root As xmlcontrol)
        Dim form As form = Nothing
        form = context.createControl(New form(Nothing))
        form.id = "id_uk_frm"
        form.action = network.thislevelPathMark
        form.method = xhtmlControl_1_1.method_enum.get
        root.addchild(form)

        Dim input As input = Nothing
        input = context.createControl(New input(Nothing))
        input.type = xhtmlctrl.input.type_enum.text
        input.name = query.url
        input.value = network.httpAddressHeader
        input.class = "c_ui"
        form.addchild(input)

        input = context.createControl(New input(Nothing))
        input.type = xhtmlctrl.input.type_enum.submit
        input.class = "c_s"
        input.value = "search"
        form.addchild(input)

        input = context.createControl(New input(Nothing))
        input.type = xhtmlctrl.input.type_enum.hidden
        input.name = query.action
        input.value = actionVerbs(action.urlKeyMapping)
        form.addchild(input)
    End Sub

    Private Function homepage(ByVal context As httpContext, ByVal page As xhtmlpage) As Boolean
        assert(Not context Is Nothing, "context is a nothing httpContext instance.")
        assert(Not page Is Nothing, "page is a nothing xhtmlpage instance.")

        page.addcsslink(network.parentPathMark + network.pathSeperator _
                        + staticrequest.generateStaticfileLinkUrl("urlshorten.css"))
        page.title().text = "urlshorten - homepage - " + applicationSign()

        Dim label As label = Nothing
        label = context.createControl(New label(Nothing))
        label.text = "Welcome to use urlshorten service in " + applicationSign()
        label.id = "id_tit"
        page.body().addchild(label)

        label = context.createControl(New label(Nothing))
        label.text = "following a small introduction."
        label.id = "id_des"
        page.body().addchild(label)

        Dim ul As ul = Nothing
        ul = context.createControl(New ul(Nothing))
        ul.id = "id_inst_ul"
        page.body().addchild(ul)

        Dim li As li = Nothing
        li = context.createControl(New li(Nothing))
        li.class = "c_insts"
        li.text = "add a mapping to a url and a shorten form."
        ul.addchild(li)
        homepage_submit(context, li)

        li = context.createControl(New li(Nothing))
        li.class = "c_insts"
        li.text = "preview a key and its original url."
        ul.addchild(li)
        homepage_preview(context, li)

        li = context.createControl(New li(Nothing))
        li.class = "c_insts"
        li.text = "try to jump to an existing shorten form [key]."
        ul.addchild(li)
        homepage_jump(context, li, False)

        li = context.createControl(New li(Nothing))
        li.class = "c_insts"
        li.text = "try to delay jump to an existing shorten form [key]."
        ul.addchild(li)
        homepage_jump(context, li, True)

        li = context.createControl(New li(Nothing))
        li.class = "c_insts"
        li.text = "get all keys mapping to an existing url."
        ul.addchild(li)
        homepage_urlkey(context, li)

        Return True
    End Function

    Private Sub urlkey_notfound(ByVal context As httpContext, ByVal page As xhtmlpage, ByVal url As String)
        Dim xmltext As xmltext = Nothing
        xmltext = context.createControl(New xmltext())
        xmltext.text = "cannot find records for url "
        page.body().addchild(xmltext)

        Dim a As a = Nothing
        a = context.createControl(New a(Nothing))
        a.text = url
        a.href = url
        page.body().addchild(a)

        xmltext = context.createControl(New xmltext())
        xmltext.text = ", this page will automatically jump to homepage of urlshorten service in 10 seconds."
        page.body().addchild(xmltext)

        delayJump_homepage(context, page)
    End Sub

    Private Function urlkey(ByVal context As httpContext, ByVal page As xhtmlpage, ByVal url As String) As Boolean
        assert(Not context Is Nothing, "context is a nothing httpContext instance.")
        assert(Not page Is Nothing, "page is a nothing xhtmlpage instance.")

        page.title().text = "urlshorten - url-key mapping - " + applicationSign()
        Dim buff As bytes = Nothing
        If Not storoom.read(url, buff) Then
            urlkey_notfound(context, page, url)
            Return True
        End If
        Dim keyall As String = Nothing
        keyall = encoding.utf8.GetString(+buff)
        Dim keyvec As vector(Of String) = Nothing
        strsepar(keyall, character.newline, keyvec, True)

        If keyvec.size() = 0 Then
            urlkey_notfound(context, page, url)
            Return True
        End If

        Dim keys As [set](Of String) = Nothing
        keys = New [set](Of String)()
        Dim i As Int64
        For i = 0 To keyvec.size() - 1
            kickLeftRightBlank(keyvec(i))
            keys.insert(keyvec(i))
        Next

        Dim xmltext As xmltext = Nothing
        xmltext = context.createControl(New xmltext())
        xmltext.text = "the url "
        page.body().addchild(xmltext)

        Dim a As a = Nothing
        a = context.createControl(New a(Nothing))
        a.text = url
        a.href = url
        page.body().addchild(a)

        xmltext = context.createControl(New xmltext())
        xmltext.text = "related to following keys."
        page.body().addchild(xmltext)

        Dim ul As ul = Nothing
        ul = context.createControl(New ul(Nothing))
        page.body().addchild(ul)

        Dim it As [set](Of String).iterator = Nothing
        it = keys.begin()
        While it <> keys.end()
            Dim li As li = Nothing
            li = context.createControl(New li(Nothing))
            ul.addchild(li)

            xmltext = context.createControl(New xmltext())
            xmltext.text = (+it) + character.blank + character.minusSign + character.blank
            li.addchild(xmltext)

            a = context.createControl(New a(Nothing))
            a.text = "preview this key"
            a.href = network.thislevelPathMark _
                    + urlUtils.combineQuery(query.key, (++it), query.action, actionVerbs(action.preview))
            li.addchild(a)

            xmltext = context.createControl(New xmltext())
            xmltext.text = character.blank + character.minusSign + character.blank
            li.addchild(xmltext)

            a = context.createControl(New a(Nothing))
            a.text = "jump to this key <same as click on the url>"
            a.href = network.thislevelPathMark _
                    + urlUtils.combineQuery(query.key, (++it), query.action, actionVerbs(action.jump))
            li.addchild(a)

            xmltext = context.createControl(New xmltext())
            xmltext.text = character.blank + character.minusSign + character.blank
            li.addchild(xmltext)

            a = context.createControl(New a(Nothing))
            a.text = "delay jump to this key"
            a.href = network.thislevelPathMark _
                    + urlUtils.combineQuery(query.key, (++it), query.action, actionVerbs(action.delayJump))
            li.addchild(a)

            it += 1
        End While

        Return True
    End Function

    Protected Overloads Overrides Function processImpl(ByVal context As httpContext _
                                                       , ByRef control As xmlctrl.xmlcontrol) As Boolean
        assert(Not context Is Nothing, "context is a nothing httpContext instance.")

        Dim key As String = Nothing
        Dim url As String = Nothing
        Dim action As action = urlshorten.action.unknown

        If strlen(context.url.path) > strlen(_pathidentity)  Then
            key = strmid(context.url.path, strlen(_pathidentity))
            action = urlshorten.action.jump
        Else
            Dim actionVerb As String = Nothing
            actionVerb = context.url.query(query.action)
            Dim i As Int64
            For i = 0 To actionVerbs.Length() - 1
                If strsame(actionVerb, actionVerbs(i), False) Then
                    action = i
                    Exit For
                End If
            Next

            url = context.url.query(query.url)
            key = context.url.query(query.key)
        End If

        control = context.createControl(New xhtmlpage(Nothing))
        Select Case action
            Case urlshorten.action.jump, urlshorten.action.delayJump
                Return jump(context, control, key, ifoper(action = urlshorten.action.jump, 0, 10))
            Case urlshorten.action.preview
                Return preview(context, control, key)
            Case urlshorten.action.submit
                Return submit(context, control, key, url)
            Case urlshorten.action.urlKeyMapping
                Return urlkey(context, control, url)
            Case urlshorten.action.unknown
                Return homepage(context, control)
            Case Else
                assert(False, "all actions should be covered in select case.")
        End Select
    End Function
End Class
